package com.auth.controller;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import com.auth.Utils.*;
import com.auth.common.BaseController;
import com.auth.common.BaseException;
import com.auth.common.Result;
import com.auth.constant.CacheConstants;
import com.auth.domain.LoginUser;
import com.auth.domain.User;
import com.auth.dto.LoginBody;
import com.auth.mapper.UserMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 登录授权
 *
 * @author jwb
 */
@RestController
public class AuthController extends BaseController {
    //未删除
    private static final Integer DELETED = 0;
    //已停用
    private static final String DISABLE = "1";

    //开启互踢
    Boolean authUnique = true;

    //设置登录失败次数，默认为5次
    private Integer attemptTimes = null;

    @Autowired
    private LoginUserHelper loginUserHelper;
    @Autowired
    private RedisService redisService;

    @Autowired
    private UserMapper userMapper;

    @PostMapping("/login")
    public Result login(@RequestBody @Validated LoginBody loginBody) throws BaseException {

        Boolean captcha = true;
        if (redisService.getCacheObject(getDisableLoginKey(loginBody)) != null) {
            Long expire = redisService.getExpire(getDisableLoginKey(loginBody), TimeUnit.SECONDS);
            throw new BaseException("重试次数过多，请" + expire + "秒后再次重试~");
        }
        // 校验验证码
        if (Boolean.TRUE.equals(captcha)) {
            if (CharSequenceUtil.isEmpty(loginBody.getCode()) || CharSequenceUtil.isEmpty(loginBody.getUuid())) {
                throw new BaseException("验证码/uuid不能为空");
            }
            String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + loginBody.getUuid();
            String code = redisService.getCacheObject(verifyKey);
            redisService.deleteObject(verifyKey);
            if (code == null) {
                throw new BaseException("验证码过期");
            }
            if (!loginBody.getCode().equalsIgnoreCase(code)) {
                throw new BaseException("验证码错误");
            }
        }

        // 查询用户信息
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("userName",loginBody.getUsername());
        User user = userMapper.selectOne(wrapper);

        // 校验账号密码
        if (DELETED.equals(user.getDelFlag())) {
            throw new BaseException("您的账号已被删除");
        }
        if (DISABLE.equals(user.getStatus())) {
            throw new BaseException("用户已停用，请联系管理员");
        }
        if (!LoginUserHelper.matchesPassword(loginBody.getPassword(), user.getPassword())) {
            // 防止密码被暴力破解，记录登录失败缓存
            loginFailCache(loginBody);
            throw new BaseException("密码错误");
        }
        // 登录成功，删除登录失败缓存记录
        clearLoginFailCache(loginBody);
        LoginUser loginUser = new LoginUser();
        loginUser.setUserId(user.getUserId());
        loginUser.setUserName(user.getUserName());
        // token过期时间
        loginUser.setExpire(36000L);
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setIpAddr(IpUtils.getIpAddr(ServletUtils.getRequest()));

        // 生成token
        String token = loginUser.getClient() + "_" + loginUser.getUserId() + "_" + IdUtils.fastUUID();
        loginUser.setToken(token);

        if (authUnique != null && authUnique) {
            // 同一账号同一终端唯一登录（互踢）
            String oldToken = redisService.getCacheObject(LoginUserHelper.getUserCachKey(loginUser.getClient(), loginUser.getUserId()));
            if (StrUtil.isNotBlank(oldToken)) {
                // 删除旧的用户缓存信息
                redisService.deleteObject(LoginUserHelper.getTokenCachKey(oldToken));
            }
        }
        redisService.setCacheObject(LoginUserHelper.getUserCachKey(loginUser.getClient(), loginUser.getUserId()), token);

        // 以token为 key 缓存用户信息
        redisService.setCacheObject(LoginUserHelper.getTokenCachKey(token), loginUser, loginUser.getExpire() != null ? loginUser.getExpire() : CacheConstants.DEFAULT_EXPIRATION, TimeUnit.SECONDS);

        // 记录日志 todo

        Map<String, Object> map = new HashMap<>();
        map.put("access_token", loginUser.getToken());
        map.put("expires_in", loginUser.getExpire());
        // 返回token
        return success(map);
    }

    /**
     * 获取禁止登录 Key
     */
    private String getDisableLoginKey(LoginBody loginBody) {
        return "DisableLogin:" + loginBody.getUsername();
    }

    /**
     * 获取尝试登录次数 Key
     */
    private String getAttemptLoginTimesKey(LoginBody loginBody) {
        return "AttemptLoginTimes:" + loginBody.getUsername();
    }

    /**
     * 登录失败存入/更新缓存
     */
    private void loginFailCache(LoginBody loginBody) {
        if (attemptTimes == null) {
            attemptTimes = 5;
        }
        Object attemptLoginTimes = redisService.getCacheObject(getAttemptLoginTimesKey(loginBody));
        if (attemptLoginTimes == null) {
            attemptLoginTimes = 0;
        }
        BigDecimal attemptLoginTimesObject = new BigDecimal(attemptLoginTimes.toString());
        int times = attemptLoginTimesObject.intValue() + 1;
        redisService.setCacheObject(getAttemptLoginTimesKey(loginBody), times);
        if (times >= attemptTimes) {
            // 连续五次输入错误，第六次不允许登录
            redisService.setCacheObject(getDisableLoginKey(loginBody), LocalDateTime.now().toString(),
                    BigDecimal.valueOf(Math.pow(5, times - (attemptTimes - 1))).longValue(), TimeUnit.SECONDS);
        }
    }

    public void clearLoginFailCache(LoginBody loginBody) {
        redisService.deleteObject(getAttemptLoginTimesKey(loginBody));
        redisService.deleteObject(getDisableLoginKey(loginBody));
    }

    @DeleteMapping("/logout")
    public Result logout(HttpServletRequest request) {
        LoginUser loginUser = loginUserHelper.getLoginUser(request);
        if (null != loginUser) {
            // 删除用户缓存记录
            loginUserHelper.delLoginUser(loginUser.getToken());
            // 记录用户退出日志 todo
        }
        return success("退出成功");
    }

//    @PostMapping("/refresh")
//    public Result refresh(HttpServletRequest request) {
//        LoginUser loginUser = userTokenService.getLoginUser(request);
//        if (null != loginUser) {
//            // 刷新令牌有效期
//            userTokenService.refreshToken(loginUser);
//            return baseResult();
//        }
//        return baseResult(SystemBaseExceptionCodes.ERROR);
//    }
}
